#include "queryserial.h"
#include "logger.h"
#include <iostream>

QuerySerial::QuerySerial()
{
    serialPort = new QSerialPort(this);
}

QuerySerial::~QuerySerial()
{
    if (mRunnig)
    {
        stop();
    }
    delete serialPort;
}

int QuerySerial::start()
{
    // 1、打开串口
    if (-1 == open_serial_with_handle())
    {
        LOG(ERROR) << "open_serial_with_handle err!" << std::endl;
        return -1;
    }
    mConnect = true;

    // 2、监控串口打开的线程
    if (false == mRunnig)
    {
        LOG(INFO) << "start monitorThreadFun thread " << mName.toStdString();
        mRunnig = true;
        monitor = std::thread(&QuerySerial::monitorThreadFun, this);
    }

    return 0;
}

int QuerySerial::stop()
{
    LOG(INFO) << "stop() start\n";
    if (mRunnig)
    {
        LOG(INFO) << "monitor.join() start\n";
        mRunnig = false;
        if (monitor.joinable())
        {
            monitor.join();
        }
        LOG(INFO) << "monitor.join() end\n";
        mRunnig = false;

        close_serial_with_handle();
        mConnect = false;
    }
    LOG(INFO) << "stop() end\n";
    return 0;
}

int QuerySerial::write_buf(char *buf, int size, int timeout)
{
    char _buf[SEND_BUF_MAX]={};
    if(size>SEND_BUF_MAX){
        LOG(ERROR)<< "can not write buf size> 512 \n";
        return -1;
    }
    memcpy(_buf,buf,size);
    evtVar.m_flag = false;
    sig_write_buf(_buf,size,timeout);

    if(false ==  evtVar.m_flag){
        int nRet = evtVar.wait(timeout);
        if(nRet == -1){
            LOG(ERROR)<< "can not write buf! \n";
            return -1;
        }
    }
    return 0;
}

int QuerySerial::open_serial()
{
    // 设置串口参数
    serialPort->setPortName(mName);
    serialPort->setBaudRate(mBaud_rate);
    serialPort->setDataBits(mData_bits);
    serialPort->setParity(mParity);
    serialPort->setStopBits(mStop_bits);
    serialPort->setFlowControl(mFlow_control);

    // 打开串口
    if (!serialPort->open(QIODevice::ReadWrite))
    {
        if (QSerialPort::PermissionError == serialPort->error())
        {
            LOG(ERROR) << "Open by other program !" << std::endl;
        }
        LOG(ERROR) << "open serial failed , error() = " << serialPort->error();
        return -1;
    }
    return 0;
}

int QuerySerial::open_serial_with_handle()
{
    if (-1 != open_serial())
    {
        connect(this,
                SIGNAL(sig_write_buf(char*, int , int)),
                this,
                SLOT(slot_write_buf(char* , int , int)));

        connect(serialPort,
                SIGNAL(errorOccurred(QSerialPort::SerialPortError)),
                this,
                SLOT(handleSerialError(QSerialPort::SerialPortError)));

        LOG(INFO) << "Open and Connect serialPort " << mName.toStdString() << " Success!";
        return 0;
    }
    return -1;
}

void QuerySerial::handleSerialError(QSerialPort::SerialPortError port_error)
{
    LOG(WARNING) << "port_error = " << port_error;
    if (port_error == QSerialPort::PermissionError)
    {
        // usb串口被拔掉了
        close_serial_with_handle();
        mConnect = false;
    }
}

int QuerySerial::close_serial_with_handle()
{
    serialPort->close();
    disconnect(serialPort,
               SIGNAL(errorOccurred(QSerialPort::SerialPortError)),
               this,
               SLOT(handleSerialError(QSerialPort::SerialPortError)));

    disconnect(this,
            SIGNAL(sig_write_buf(char *, int , int)),
            this,
            SLOT(slot_write_buf(char *, int , int)));
    return 0;
}

void QuerySerial::monitorThreadFun()
{
    while (mRunnig)
    {
        if (mConnect)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }
        else
        {
            if (mName != "")
            {
                // 打开串口
                if (-1 == open_serial_with_handle())
                {
                    LOG(ERROR) << "open_serial_with_handle err!" << std::endl;
                    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
                    continue;
                }
                mConnect = true;
            }
        }
    }
}

void QuerySerial::set_params(std::string com_name, QSerialPort::BaudRate baud_rate, QSerialPort::DataBits data_bits, QSerialPort::Parity parity, QSerialPort::StopBits stop_bits, QSerialPort::FlowControl flow_control)
{
    mName = QString::fromStdString(com_name);
    mBaud_rate = baud_rate;
    mData_bits = data_bits;
    mParity = parity;
    mStop_bits = stop_bits;
    mFlow_control = flow_control;

    LOG(INFO) << "mName: " << mName.toStdString() << std::endl;
    LOG(INFO) << "mBaud_rate: " << mBaud_rate << std::endl;
    LOG(INFO) << "mData_bits: " << mData_bits << std::endl;
    LOG(INFO) << "mParity: " << mParity << std::endl;
    LOG(INFO) << "mStop_bits: " << mStop_bits << std::endl;
    LOG(INFO) << "mFlow_control: " << mFlow_control << std::endl;
}

int QuerySerial::_write_buf(char *buf, int size, int timeout)
{
    if (size <= 0 || nullptr == buf)
    {
        LOG(ERROR) << "Input err: size " << size << "buf " << buf << std::endl;
        return -1;
    }
    if (!mConnect)
    {
        LOG(ERROR) << "Dev mId_path " << mId_path << " not connetted! " << std::endl;
        return -1;
    }

    QByteArray data(buf, size);
    qint64 bytesWritten = serialPort->write(data);
    if (bytesWritten < 0)
    {
        LOG(ERROR) << "Write err! bytesWritten " << bytesWritten << std::endl;
        return -1;
    }

    // 等待timeout，至少有1个字节被写入
    if (!serialPort->waitForBytesWritten(timeout))
    {
        LOG(ERROR) << "Write time out: " << serialPort->errorString().toStdString() << std::endl;
        return -1;
    }
    //LOG(INFO) << "bytesWritten: " << bytesWritten << std::endl;
    return 0;
}

int QuerySerial::read_buf(char *buf, int size, int timeout)
{
    if (size <= 0 || nullptr == buf)
    {
        LOG(ERROR) << "Input err: size " << size << "buf " << buf << std::endl;
        return -1;
    }
    if (!mConnect)
    {
        LOG(ERROR) << "Dev mId_path " << mId_path << " not connetted! " << std::endl;
        return -1;
    }

    int time = timeout / 10;
    while (serialPort->bytesAvailable() < size && time--)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    if (time <= 0)
    {
        LOG(ERROR) << "read buf time out! serialPort->bytesAvailable : " << serialPort->bytesAvailable() << std::endl;
    }

    return serialPort->read(buf, size);
}

void QuerySerial::slot_write_buf(char *buf, int size, int timeout)
{
    if(-1 == _write_buf(buf, size, timeout)){
        LOG(ERROR) << "_write_buf Err!" << std::endl;
    }else{
        evtVar.notify();
    }
}
